#include "54func.h"
#include "clientFunc.h"

#define FILELIMIT 100000000

extern addrInfo_t addrInfo;

// 在目录路径后添加文件名,filePath为目录路径,argv为文件路径
int concatFileName(const char *argv,char *filePath)
{
    int fileSize = strlen(filePath);
    int length = strlen(argv);
    while(length > 0){
        if(argv[length-1] != '/') { --length; }
        else { break; }
    }
    if(filePath[fileSize-1] != '/'){
        sprintf(filePath+fileSize,"%s","/");
        ++fileSize;
    }
    sprintf(filePath+fileSize,"%s",argv+length);
    return 0;
}

// 非mmap版本transFile
int transFile(int sockfd, int filefd)
{
    printf("小文件普通传输!\n");
    train_t train;
    if(recvMsg(sockfd,&train) == 0){
        // 'e'处理报错
        return -1;
    }
    // 'o'正常操作
    // 先收lseek长度
    recvn(sockfd,&train.length,sizeof(train.length));
    recvn(sockfd,train.data,train.length);
    // 断点续传lseek
    off_t offset;
    memcpy(&offset,train.data,train.length);
    if(offset > 0){
        printf("触发断点续传!\n");
    }
    lseek(filefd,offset,SEEK_SET);
    // 传输文件
    while(1){
        ssize_t sret = read(filefd,train.data,TRAINLEN);
        train.length = sret;
        ssize_t sret1 = send(sockfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);
        if(sret == 0 || sret1 == -1){
            // 发完空包或者触发SIGPIPE退出
            break;
        }
    }
    close(filefd);
    return 0;
}
// mmap版本transFile
int transFileMmap(int sockfd, int filefd, off_t fileSize)
{
    printf("大文件Mmap传输!\n");
    train_t train;
    if(recvMsg(sockfd,&train) == 0){
        // 'e'处理报错
        return -1;
    }
    // 'o'正常操作
    // 先收lseek长度
    recvn(sockfd,&train.length,sizeof(train.length));
    recvn(sockfd,train.data,train.length);
    // 断点续传lseek
    off_t offset;
    memcpy(&offset,train.data,train.length);
    if(offset > 0){
        printf("触发断点续传!\n");
    }
    //lseek(filefd,offset,SEEK_SET);
    // 发送文件内容
    char *p = (char *)mmap(NULL,fileSize,PROT_READ|PROT_WRITE,MAP_SHARED,filefd,0);
    ERROR_CHECK(p,MAP_FAILED,"mmap");
    send(sockfd, p+offset, fileSize-offset, MSG_NOSIGNAL);
    munmap(p,fileSize);
    //sendfile(sockfd,filefd,NULL,fileSize);
    close(filefd);
    return 0;
}

// 发送文件,cmd_t获取指令及参数
int clientPuts(int sockfd, cwd_t *pcwd, cmd_t *pcmd)
{
    char pathStr[1024] = {0};
    train_t train;
    // 1 先处理参数
    if(pcmd->argc == 1) {
        // 默认cwd路径
        getPath(pcwd,pcmd->argv2);
    }
    else if(pcmd->argc == 2){
        // 指定路径
        parsePath(pcwd, pcmd->argv2, pathStr);
        memset(pcmd->argv2, 0, sizeof(pcmd->argv2));
        strcpy(pcmd->argv2, pathStr);
    }
    // 2 发送cmd_t
    send(sockfd,pcmd,sizeof(cmd_t),MSG_NOSIGNAL);
    // 3 发sha1值
    sha1Send(sockfd,pcmd->argv1);
    // 4 第一次recvMsg
    // 4.1 "e"+msg
    if(recvMsg(sockfd,&train) == 0){ return -1; }
    // 第二次recvMsg
    if(recvMsg(sockfd,&train) == 1){
        // 4.2.1 "o"+"o"秒传
        printf("急速秒传!\n");
    }
    else{
        // 4.2.2 "o"+"e"普通上传,调用函数
        // 获取文件名
        char fileName[PATH_MAX] = {0};
        realPath(pcmd->argv1,fileName);
        // 获取文件大小
        int filefd = open(fileName,O_RDWR);
        ERROR_CHECK(filefd,-1,"open in clientPuts");
        struct stat statbuf;
        fstat(filefd,&statbuf);
        // 发送文件长度
        train.length = sizeof(statbuf.st_size);
        memcpy(train.data,&statbuf.st_size,train.length);
        send(sockfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);
        // 根据大小选择适合的发送版本
        if(statbuf.st_size > FILELIMIT){
            transFileMmap(sockfd,filefd,statbuf.st_size);
        }
        else{
            transFile(sockfd,filefd);
        }
    }
    return 0;
}

// 下载文件clientGets
// 非mmap版recvFile
int recvFile(int sockfd, const char* fileName)
{
    train_t train;
    // 文件已存在就续传，不存在则新建文件
    int filefd = open(fileName,O_RDWR|O_CREAT,0666);
    ERROR_CHECK(filefd,-1,"open in recvFile");
    // 告知Client正常执行以及需要lseek的长度
    successSend(sockfd);
    off_t offset = lseek(filefd,0,SEEK_END);
    ERROR_CHECK(offset,-1,"lseek in recvFile");
    train.length = sizeof(offset);
    memcpy(train.data,&offset,train.length);
    send(sockfd,&train,sizeof(train.length)+train.length,MSG_NOSIGNAL);
    // 接收文件内容
    while(1){
        // recv
        recvn(sockfd,&train.length,sizeof(train.length));
        if(train.length == 0){  // 读完会读到空包就退出循环
            break;
        }
        recvn(sockfd,train.data,train.length);
        // write
        ssize_t sret = write(filefd,train.data,train.length);
        ERROR_CHECK(sret,-1,"write in recvFile");
    }
    close(filefd);
    return 0;
}

// 接收文件,cwd_t获取cwd,cmd_t获取指令及参数,包含断点续传
int clientGets(int sockfd, cmd_t *pcmd)
{
    char pathStr[1024] = {0}; 
    train_t train;
    // 1 先处理参数
    //realServerPath(&cwd,pcmd->argv1);
    parsePath(&cwd, pcmd->argv1, pathStr);
    memset(pcmd->argv1, 0, sizeof(pcmd->argv1));
    strcpy(pcmd->argv1, pathStr);
    // 2 发cmd_t
    send(sockfd,pcmd,sizeof(cmd_t),MSG_NOSIGNAL);
    // 3 先收"e"/"o"
    int ret = recvMsg(sockfd,&train);
    // 3.1 "e"+msg;
    if(ret == 0){
        return -1;
    }
    // 3.2 "o"+收文件
    // 获取文件路径
    char fileName[PATH_MAX] = {0};
    if(pcmd->argc == 1){
        // 默认cwd路径
        char cwd[1024] = {0};
        getcwd(cwd,sizeof(cwd));
        sprintf(fileName,"%s%s",cwd,"/");
    }
    else if(pcmd->argc == 2){
        // 指定路径
        // 检查第一个字符是否是~
        if(pcmd->argv2[0] == '~'){
            printf("暂不支持识别~,请将~更换成/home/用户名!\n");
            return -1;
        }
        realPath(pcmd->argv2,fileName);
    }
    // 接上文件名
    concatFileName(pcmd->argv1,fileName);
    // 接收文件大小
    off_t fileSize;
    recvn(sockfd,&train.length,sizeof(train.length));
    recvn(sockfd,train.data,train.length);
    memcpy(&fileSize,train.data,train.length);
    // 非mmap接收文件
    recvFile(sockfd,fileName);
    return 0;
}

void* transThreadFunc(void *arg) {
    cmd_t* pcmd = (cmd_t*) arg;
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in serverAddr;
    serverAddr.sin_addr.s_addr = inet_addr(addrInfo.ip);
    serverAddr.sin_port = htons(atoi(addrInfo.port));
    serverAddr.sin_family = AF_INET;
    int ret = connect(sockfd,(struct sockaddr *)&serverAddr,sizeof(serverAddr));
    ERROR_CHECK(ret,-1,"connect");

    // "e"+msg 客户端子线程发起连接
    errorSend(sockfd, NULL);
    if(strcmp(pcmd->cmdToken, "puts") == 0) {
        errorSend(sockfd, "接收文件任务!");
        ret = clientPuts(sockfd, &cwd, pcmd);
        if(ret == 0){
            printf("上传完毕!\n");
        }
    }
    else if(strcmp(pcmd->cmdToken, "gets") == 0) {
        errorSend(sockfd, "发送文件任务!");
        ret = clientGets(sockfd, pcmd);
        if(ret == 0){
            printf("下载完毕!\n");
        }
    }
    pthread_exit(NULL);
}

int makeTransRequest(cmd_t* pcmd) {
    // 自动回收资源的子线程
    pthread_t thread_id;
    pthread_attr_t attr;
    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);

    pthread_create(&thread_id, &attr, transThreadFunc, pcmd);

    pthread_attr_destroy(&attr);
    return 0;
}
